#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "Cube.h"

char cube[NUM_SIDE][NUM_SIZE][NUM_SIZE];
char color[NUM_SIDE] = {'Y', 'O', 'B', 'R', 'G', 'W'};
char *name[NUM_SIDE] = {"Up", "Front", "Right", "Back", "Left", "Down"};

unsigned long long n;

/*int main(int argc, char** argv) {
	
	char *byteCode = loadFile("Moves", SIZE);
	
	//char *byteCode = "ufL";
	
	cubeInit();
	
	int i = turnByteCode(byteCode);
	printf("Turns: %d\n\n", i);
	
	int s = cubeScore();
	printf("Score: %d\n", s);
	
	cubePrint();
	
	free(byteCode);
	
	return 0;
}*/

char *loadFile(char *fileName, int size) {
	char *data = malloc(size + 1);
	
	data[size] = 0;
	
	FILE *f = fopen(fileName, "r");
	
	if (fread(data, size, 1, f) != 1) {
		printf("Error while reading from file.\n");
		return 0;
	}
	
	fclose(f);
	
	return data;
}

void randomize() {
	n = clock() + time(NULL);
}

int nextRandom(int m) {
	n = (n * n + 1) % (2617 * 67411);
	return n % m;
}

int turnByteCode(char *byteCode) {
	int i = 0;
	while (1)
		switch (byteCode[i++]) {
			case 'u': turnClockwise(SIDE_UP); break;
			case 'f': turnClockwise(SIDE_FRONT); break;
			case 'r': turnClockwise(SIDE_RIGHT); break;
			case 'b': turnClockwise(SIDE_BACK); break;
			case 'l': turnClockwise(SIDE_LEFT); break;
			case 'd': turnClockwise(SIDE_DOWN); break;
			case 'U': turnCounterClockwise(SIDE_UP); break;
			case 'F': turnCounterClockwise(SIDE_FRONT); break;
			case 'R': turnCounterClockwise(SIDE_RIGHT); break;
			case 'B': turnCounterClockwise(SIDE_BACK); break;
			case 'L': turnCounterClockwise(SIDE_LEFT); break;
			case 'D': turnCounterClockwise(SIDE_DOWN); break;
			case 0: return i - 1;
		}
}

void cubeInit() {
	int side, x, y;
	for (side = 0; side < NUM_SIDE; side++)
		for (x = 0; x < NUM_SIZE; x++)
			for (y = 0; y < NUM_SIZE; y++)
				cube[side][x][y] = color[side];
}

void cubeRandomize() {
	
	randomize();
	
	int i;
	for (i = 0; i < 50; i++) {
		
		int t = nextRandom(NUM_MOVE);
		
		if (t < NUM_SIDE)
			turnClockwise(t);
		else
			turnCounterClockwise(t - NUM_SIDE);
	}
}

void cubePrint() {
	int side, x, y;
	for (side = 0; side < NUM_SIDE; side++) {
		printf("%s:\n", name[side]);
		for (y = 0; y < NUM_SIZE; y++) {
			for (x = 0; x < NUM_SIZE; x++)
				printf(" %c", cube[side][x][y]);
			printf("\n");
		}
		printf("\n");
	}
}

int cubeScore() {
	int s = 0;
	
	int side, x, y;
	for (side = 0; side < NUM_SIDE; side++)
		for (x = 0; x < NUM_SIZE; x++)
			for (y = 0; y < NUM_SIZE; y++)
				if (cube[side][x][y] != color[side])
					s++;
	
	return s;
}

void turnClockwise(int side) {
	//Turn side colors. 
	turnClockwiseSide(side);
	
	//Switch rows.
	turnClockwiseRows(side);
}

void turnCounterClockwise(int side) {
	//Turn side colors. 
	turnCounterClockwiseSide(side);
	
	//Switch rows.
	turnCounterClockwiseRows(side);
}

void turnClockwiseSide(int side) {
	//Switch top corner colors.
	char t = cube[side][0][0];
	cube[side][0][0] = cube[side][0][2];
	cube[side][0][2] = cube[side][2][2];
	cube[side][2][2] = cube[side][2][0];
	cube[side][2][0] = t;
	
	//Switch top middel colors.
	t = cube[side][1][0];
	cube[side][1][0] = cube[side][0][1];
	cube[side][0][1] = cube[side][1][2];
	cube[side][1][2] = cube[side][2][1];
	cube[side][2][1] = t;
}

void turnCounterClockwiseSide(int side) {
	//Switch top corner colors.
	char t = cube[side][0][0];
	cube[side][0][0] = cube[side][2][0];
	cube[side][2][0] = cube[side][2][2];
	cube[side][2][2] = cube[side][0][2];
	cube[side][0][2] = t;
	
	//Switch top middel colors.
	t = cube[side][1][0];
	cube[side][1][0] = cube[side][2][1];
	cube[side][2][1] = cube[side][1][2];
	cube[side][1][2] = cube[side][0][1];
	cube[side][0][1] = t;
}

void turnClockwiseRows(int side) {
	int i;
	char t;
	
	switch (side) {
		case SIDE_UP:
			for (i = 0; i < NUM_SIZE; i++) {
				t = cube[SIDE_LEFT][i][0];
				cube[SIDE_LEFT ][i][0] = cube[SIDE_FRONT][i][0];
				cube[SIDE_FRONT][i][0] = cube[SIDE_RIGHT][i][0];
				cube[SIDE_RIGHT][i][0] = cube[SIDE_BACK ][i][0];
				cube[SIDE_BACK ][i][0] = t;
			}
			break;
		
		case SIDE_FRONT:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_UP][i][2];
				cube[SIDE_UP   ][i][2] = cube[SIDE_LEFT ][2][n];
				cube[SIDE_LEFT ][2][n] = cube[SIDE_DOWN ][n][0];
				cube[SIDE_DOWN ][n][0] = cube[SIDE_RIGHT][0][i];
				cube[SIDE_RIGHT][0][i] = t;
			}
			break;
		
		case SIDE_RIGHT:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_UP][2][n];
				cube[SIDE_UP   ][2][n] = cube[SIDE_FRONT][2][n];
				cube[SIDE_FRONT][2][n] = cube[SIDE_DOWN ][2][n];
				cube[SIDE_DOWN ][2][n] = cube[SIDE_BACK ][0][i];
				cube[SIDE_BACK ][0][i] = t;
			}
			break;
		
		case SIDE_BACK:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_UP][n][0];
				cube[SIDE_UP   ][n][0] = cube[SIDE_RIGHT][2][n];
				cube[SIDE_RIGHT][2][n] = cube[SIDE_DOWN ][i][2];
				cube[SIDE_DOWN ][i][2] = cube[SIDE_LEFT ][0][i];
				cube[SIDE_LEFT ][0][i] = t;
			}
			break;
		
		case SIDE_LEFT:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_UP][0][i];
				cube[SIDE_UP   ][0][i] = cube[SIDE_BACK ][2][n];
				cube[SIDE_BACK ][2][n] = cube[SIDE_DOWN ][0][i];
				cube[SIDE_DOWN ][0][i] = cube[SIDE_FRONT][0][i];
				cube[SIDE_FRONT][0][i] = t;
			}
			break;
		
		case SIDE_DOWN:
			for (i = 0; i < NUM_SIZE; i++) {
				t = cube[SIDE_LEFT][i][2];
				cube[SIDE_LEFT ][i][2] = cube[SIDE_BACK ][i][2];
				cube[SIDE_BACK ][i][2] = cube[SIDE_RIGHT][i][2];
				cube[SIDE_RIGHT][i][2] = cube[SIDE_FRONT][i][2];
				cube[SIDE_FRONT][i][2] = t;
			}
			break;
	}
}

void turnCounterClockwiseRows(int side) {
	int i;
	char t;
	
	switch (side) {
		case SIDE_UP:
			for (i = 0; i < NUM_SIZE; i++) {
				t = cube[SIDE_BACK][i][0];
				cube[SIDE_BACK ][i][0] = cube[SIDE_RIGHT][i][0];
				cube[SIDE_RIGHT][i][0] = cube[SIDE_FRONT][i][0];
				cube[SIDE_FRONT][i][0] = cube[SIDE_LEFT ][i][0];
				cube[SIDE_LEFT ][i][0] = t;
			}
			break;
		
		case SIDE_FRONT:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_RIGHT][0][i];
				cube[SIDE_RIGHT][0][i] = cube[SIDE_DOWN][n][0];
				cube[SIDE_DOWN ][n][0] = cube[SIDE_LEFT][2][n];
				cube[SIDE_LEFT ][2][n] = cube[SIDE_UP  ][i][2];
				cube[SIDE_UP   ][i][2] = t;
			}
			break;
		
		case SIDE_RIGHT:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_BACK][0][i];
				cube[SIDE_BACK ][0][i] = cube[SIDE_DOWN ][2][n];
				cube[SIDE_DOWN ][2][n] = cube[SIDE_FRONT][2][n];
				cube[SIDE_FRONT][2][n] = cube[SIDE_UP   ][2][n];
				cube[SIDE_UP   ][2][n] = t;
			}
			break;
		
		case SIDE_BACK:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_LEFT][0][i];
				cube[SIDE_LEFT ][0][i] = cube[SIDE_DOWN ][i][2];
				cube[SIDE_DOWN ][i][2] = cube[SIDE_RIGHT][2][n];
				cube[SIDE_RIGHT][2][n] = cube[SIDE_UP   ][n][0];
				cube[SIDE_UP   ][n][0] = t;
			}
			break;
		
		case SIDE_LEFT:
			for (i = 0; i < NUM_SIZE; i++) {
				int n = 2 - i;
				t = cube[SIDE_FRONT][0][i];
				cube[SIDE_FRONT][0][i] = cube[SIDE_DOWN][0][i];
				cube[SIDE_DOWN ][0][i] = cube[SIDE_BACK][2][n];
				cube[SIDE_BACK ][2][n] = cube[SIDE_UP  ][0][i];
				cube[SIDE_UP   ][0][i] = t;
			}
			break;
		
		case SIDE_DOWN:
			for (i = 0; i < NUM_SIZE; i++) {
				t = cube[SIDE_FRONT][i][2];
				cube[SIDE_FRONT][i][2] = cube[SIDE_RIGHT][i][2];
				cube[SIDE_RIGHT][i][2] = cube[SIDE_BACK ][i][2];
				cube[SIDE_BACK ][i][2] = cube[SIDE_LEFT ][i][2];
				cube[SIDE_LEFT ][i][2] = t;
			}
			break;
	}
}

